package com.hzw.core.rom

import android.Manifest.permission
import android.annotation.SuppressLint
import android.content.Intent
import android.content.res.Configuration
import android.content.res.Resources
import android.net.Uri
import android.os.Build
import android.provider.Settings
import android.text.TextUtils
import androidx.annotation.RequiresApi
import androidx.annotation.RequiresPermission
import com.hzw.core.ctx.appCtx
import com.hzw.core.log.logE
import com.hzw.core.services.telephonyManager
import com.hzw.core.services.wifiManager
import com.hzw.core.store.DataPreferences
import java.io.File
import java.net.InetAddress
import java.net.NetworkInterface
import java.net.SocketException
import java.util.*

fun isDeviceRooted(): Boolean {
    val su = "su"
    val locations = arrayOf(
        "/system/bin/", "/system/xbin/", "/sbin/", "/system/sd/xbin/",
        "/system/bin/failsafe/", "/data/local/xbin/", "/data/local/bin/", "/data/local/",
        "/system/sbin/", "/usr/bin/", "/vendor/bin/"
    )
    for (location in locations) {
        if (File(location + su).exists()) {
            return true
        }
    }
    return false
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
fun isAdbEnabled(): Boolean {
    return Settings.Secure.getInt(appCtx.contentResolver, Settings.Global.ADB_ENABLED, 0) > 0
}

/**
 * Return the android id of device.
 *
 * @return the android id of device
 */
@SuppressLint("HardwareIds")
fun getAndroidID(): String {
    val id = Settings.Secure.getString(
        appCtx.contentResolver,
        Settings.Secure.ANDROID_ID
    )
    return if ("9774d56d682e549c" == id) "" else id ?: ""
}

/**
 * Return the MAC address.
 *
 * Must hold `<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />`,
 * `<uses-permission android:name="android.permission.INTERNET" />`,
 * `<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />`
 *
 * @return the MAC address
 */
@RequiresPermission(allOf = [permission.ACCESS_WIFI_STATE, permission.INTERNET, permission.CHANGE_WIFI_STATE])
fun getMacAddress(): String {
    val macAddress = getMacAddress(*arrayOf())
    if (macAddress != "" || getWifiEnabled()) return macAddress
    setWifiEnabled(true)
    setWifiEnabled(false)
    return getMacAddress(*arrayOf())
}

private fun getWifiEnabled(): Boolean {
    return wifiManager?.isWifiEnabled ?: false
}

/**
 * wifi开关
 * 在Android Q 后废弃，不允许APP控制
 *
 * Must hold `<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />`
 *
 * @param enabled True to enabled, false otherwise.
 */
@RequiresPermission(permission.CHANGE_WIFI_STATE)
private fun setWifiEnabled(enabled: Boolean) {
    @SuppressLint("WifiManagerLeak")
    val manager = wifiManager ?: return
    if (enabled == manager.isWifiEnabled) return
    manager.isWifiEnabled = enabled
}

/**
 * Return the MAC address.
 *
 * Must hold `<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />`,
 * `<uses-permission android:name="android.permission.INTERNET" />`
 *
 * @return the MAC address
 */
@RequiresPermission(allOf = [permission.ACCESS_WIFI_STATE, permission.INTERNET])
fun getMacAddress(vararg excepts: String): String {
    var macAddress = getMacAddressByNetworkInterface()
    if (isAddressNotInExcepts(macAddress, *excepts)) {
        return macAddress
    }
    macAddress = getMacAddressByInetAddress()
    if (isAddressNotInExcepts(macAddress, *excepts)) {
        return macAddress
    }
    macAddress = getMacAddressByWifiInfo()
    if (isAddressNotInExcepts(macAddress, *excepts)) {
        return macAddress
    }
    return ""
}

private fun isAddressNotInExcepts(address: String, vararg excepts: String): Boolean {
    if (excepts.isEmpty()) {
        return "02:00:00:00:00:00" != address
    }
    for (filter in excepts) {
        if (address == filter) {
            return false
        }
    }
    return true
}

@SuppressLint("MissingPermission", "HardwareIds")
private fun getMacAddressByWifiInfo(): String {
    try {
        val wifi = wifiManager
        if (wifi != null) {
            val info = wifi.connectionInfo
            if (info != null) return info.macAddress
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
    return "02:00:00:00:00:00"
}

private fun getMacAddressByNetworkInterface(): String {
    try {
        val nis = NetworkInterface.getNetworkInterfaces()
        while (nis.hasMoreElements()) {
            val ni = nis.nextElement()
            if (ni == null || !ni.name.equals("wlan0", ignoreCase = true)) continue
            val macBytes = ni.hardwareAddress
            if (macBytes != null && macBytes.isNotEmpty()) {
                val sb = StringBuilder()
                for (b in macBytes) {
                    sb.append(String.format("%02x:", b))
                }
                return sb.substring(0, sb.length - 1)
            }
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
    return "02:00:00:00:00:00"
}

private fun getMacAddressByInetAddress(): String {
    try {
        val inetAddress = getInetAddress()
        if (inetAddress != null) {
            val ni = NetworkInterface.getByInetAddress(inetAddress)
            if (ni != null) {
                val macBytes = ni.hardwareAddress
                if (macBytes != null && macBytes.isNotEmpty()) {
                    val sb = StringBuilder()
                    for (b in macBytes) {
                        sb.append(String.format("%02x:", b))
                    }
                    return sb.substring(0, sb.length - 1)
                }
            }
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
    return "02:00:00:00:00:00"
}

private fun getInetAddress(): InetAddress? {
    try {
        val nis = NetworkInterface.getNetworkInterfaces()
        while (nis.hasMoreElements()) {
            val ni = nis.nextElement()
            // To prevent phone of xiaomi return "10.0.2.15"
            if (!ni.isUp) continue
            val addresses = ni.inetAddresses
            while (addresses.hasMoreElements()) {
                val inetAddress = addresses.nextElement()
                if (!inetAddress.isLoopbackAddress) {
                    val hostAddress = inetAddress.hostAddress
                    if (hostAddress.indexOf(':') < 0) return inetAddress
                }
            }
        }
    } catch (e: SocketException) {
        e.printStackTrace()
    }
    return null
}


/**
 * Return an ordered list of ABIs supported by this device. The most preferred ABI is the first
 * element in the list.
 *
 * @return an ordered list of ABIs supported by this device
 */
fun getABIs(): Array<String?>? {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        Build.SUPPORTED_ABIS
    } else {
        if (!TextUtils.isEmpty(Build.CPU_ABI2)) {
            arrayOf(Build.CPU_ABI, Build.CPU_ABI2)
        } else arrayOf(Build.CPU_ABI)
    }
}

/**
 * Return whether device is tablet.
 *
 * @return `true`: yes<br></br>`false`: no
 */
fun isTablet(): Boolean {
    return ((Resources.getSystem().configuration.screenLayout
            and Configuration.SCREENLAYOUT_SIZE_MASK)
            >= Configuration.SCREENLAYOUT_SIZE_LARGE)
}

/**
 * Return whether device is emulator.
 *
 * @return `true`: yes<br></br>`false`: no
 */
fun isEmulator(): Boolean {
    val checkProperty = (Build.FINGERPRINT.startsWith("generic")
            || Build.FINGERPRINT.toLowerCase().contains("vbox")
            || Build.FINGERPRINT.toLowerCase().contains("test-keys")
            || Build.MODEL.contains("google_sdk")
            || Build.MODEL.contains("Emulator")
            || Build.MODEL.contains("Android SDK built for x86")
            || Build.MANUFACTURER.contains("Genymotion")
            || Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")
            || "google_sdk" == Build.PRODUCT)
    if (checkProperty) return true
    var operatorName = ""
    val tm = telephonyManager
    val name = tm.networkOperatorName
    if (name != null) {
        operatorName = name
    }
    val checkOperatorName = operatorName.toLowerCase() == "android"
    if (checkOperatorName) return true
    val url = "tel:" + "123456"
    val intent = Intent()
    intent.data = Uri.parse(url)
    intent.action = Intent.ACTION_DIAL
    return intent.resolveActivity(appCtx.packageManager) == null
}


private const val KEY_UDID = "KEY_UDID"

@Volatile
private var udid: String = ""

/**
 * Return the unique device id.
 * <pre>{1}{UUID(macAddress)}</pre>
 * <pre>{2}{UUID(androidId )}</pre>
 * <pre>{9}{UUID(random    )}</pre>
 *
 * @return the unique device id
 */
@SuppressLint("MissingPermission", "HardwareIds")
fun getUniqueDeviceId(): String {
    return getUniqueDeviceId("", true)
}

/**
 * Return the unique device id.
 * <pre>android 10 deprecated {prefix}{1}{UUID(macAddress)}</pre>
 * <pre>{prefix}{2}{UUID(androidId )}</pre>
 * <pre>{prefix}{9}{UUID(random    )}</pre>
 *
 * @param prefix The prefix of the unique device id.
 * @return the unique device id
 */
@SuppressLint("MissingPermission", "HardwareIds")
fun getUniqueDeviceId(prefix: String): String? {
    return getUniqueDeviceId(prefix, true)
}

/**
 * Return the unique device id.
 * <pre>{1}{UUID(macAddress)}</pre>
 * <pre>{2}{UUID(androidId )}</pre>
 * <pre>{9}{UUID(random    )}</pre>
 *
 * @param useCache True to use cache, false otherwise.
 * @return the unique device id
 */
@SuppressLint("MissingPermission", "HardwareIds")
fun getUniqueDeviceId(useCache: Boolean): String? {
    return getUniqueDeviceId("", useCache)
}

val lock = Any()

/**
 * Return the unique device id.
 * <pre>android 10 deprecated {prefix}{1}{UUID(macAddress)}</pre>
 * <pre>{prefix}{2}{UUID(androidId )}</pre>
 * <pre>{prefix}{9}{UUID(random    )}</pre>
 *
 * @param prefix   The prefix of the unique device id.
 * @param useCache True to use cache, false otherwise.
 * @return the unique device id
 */
@SuppressLint("MissingPermission", "HardwareIds")
fun getUniqueDeviceId(prefix: String, useCache: Boolean): String {
    if (!useCache) {
        return getUniqueDeviceIdReal(prefix)
    }
    if (udid.isBlank()) {
        synchronized(lock) {
            if (udid.isBlank()) {
                val id: String = DataPreferences.getString(KEY_UDID)
                if (id.isNotBlank()) {
                    udid = id
                    return udid
                }
                return getUniqueDeviceIdReal(prefix)
            }
        }
    }
    return udid
}

private fun getUniqueDeviceIdReal(prefix: String): String {
    try {
        val androidId = getAndroidID()
        if (androidId.isNotBlank()) {
            return saveUdid(prefix + 2, androidId)
        }
    } catch (ignore: Exception) {
        ignore.logE()
    }
    return saveUdid(prefix + 9, "")
}

@SuppressLint("MissingPermission", "HardwareIds")
fun isSameDevice(uniqueDeviceId: String): Boolean {
    // {prefix}{type}{32id}
    if (TextUtils.isEmpty(uniqueDeviceId) && uniqueDeviceId.length < 33) return false
    if (uniqueDeviceId == udid) return true
    val cachedId: String = DataPreferences.getString(KEY_UDID)
    if (uniqueDeviceId == cachedId) return true
    val st = uniqueDeviceId.length - 33
    val type = uniqueDeviceId.substring(st, st + 1)
    if (type.startsWith("1")) {
        val macAddress = getMacAddress()
        return if (macAddress == "") {
            false
        } else uniqueDeviceId.substring(st + 1) == getUdid("", macAddress)
    } else if (type.startsWith("2")) {
        val androidId = getAndroidID()
        return if (TextUtils.isEmpty(androidId)) {
            false
        } else uniqueDeviceId.substring(st + 1) == getUdid("", androidId)
    }
    return false
}

private fun saveUdid(prefix: String, id: String): String {
    udid = getUdid(prefix, id)
    DataPreferences.putString(KEY_UDID, udid)
    return udid
}

private fun getUdid(prefix: String, id: String): String {
    return if (id == "") {
        prefix + UUID.randomUUID().toString().replace("-", "")
    } else {
        prefix + UUID.nameUUIDFromBytes(id.toByteArray()).toString().replace("-", "")
    }
}